-------------------------------------------------------------------------------
-- Title      : Program Counter
-- Project    : ANEM 16 bits
-------------------------------------------------------------------------------
-- File       : PC.vhd
-- Author     : Ian Antônio
-- Company    : 
-- Created    : 2011-04-16
-- Last update: 2011-04-29
-- Platform   : 
-- Standard   : VHDL'87
-------------------------------------------------------------------------------
-- Description: Descrição do contador de programa do ANEM 16 bits mono-ciclo.
-------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity PC is
port(	test		:in std_logic;				--Sinal de controle do teste/debug.
	s_in_pc		:in std_logic;				--Sinal de entrada do teste/debug.
	s_out_pc	:out std_logic;				--Sinal de saída do teste/debug.
	pc_cont		:in std_logic_vector(1 downto 0);	--Operação
  	endereco	:in std_logic_vector(11 downto 0);	--Jump J
  	a_out		:in std_logic_vector(15 downto 0);	--Jump JR
  	offset		:in std_logic_vector(3 downto 0);	--Jump BEQ - Offset a ser somado no contador.
 	z		:in std_logic;				--Jump BEQ - Indica se haverá jump.
  	rst, clk	:in std_logic;				--Reset e Clock
  	pc_out		:out std_logic_vector(15 downto 0));	--Saída para a memória de programa.
end entity PC;

architecture behavior of PC is
begin
  process(clk,rst)					
  variable counter	:std_logic_vector(15 downto 0)	:=	(others=>'0');		--Guarda o valor do PC
  variable carry	:std_logic_vector(16 downto 0) := (others=>'0');		--Usado nas diversas somas
  variable offlong	:std_logic_vector(15 downto 0);					--Aumenta o tamanho de 'offset' para 16 bits
  begin		
    if rst='1' then					--Sensível ao reset 
      counter := (others=>'0');	
      s_out_pc <= '0';
    elsif rising_edge(clk) then				--Sensível à subida do clock
      if test='1' then					--Caso esteja em modo 'debug'
	s_out_pc <= counter(0);	
	counter(14 downto 0) := counter(15 downto 1);
	counter(15) := s_in_pc;
      else						--Caso não esteja em modo 'debug'
       s_out_pc <= '0';
       case pc_cont is 						--Operações:
        when "00" =>	carry(0) := '1';					--Sem jump (incrementa-se o PC)	
          for i in 0 to 15 loop
            carry(i+1) := counter(i) and carry(i);
            counter(i) := counter(i) xor carry(i);
          end loop;
          carry := (others=>'0');
        when "01" =>	counter (11 downto 0) := endereco;			--Jump J (endereço dado pelo vetor de instrução)
        when "10" =>	counter := a_out;					--Jump JR (endereço dado pelo registrador A)
        when "11" =>	if z='1' then						--Jump BEQ
            offlong(3 downto 0) := offset;						--Se z=1, fazer PC+OFFSET.			
            if offset(3) = '1' then					  			--Se for número negativo, concatenar '1' e somar.
              offlong(15 downto 4) := (others=>'1');
            else							  			--Se for número positivo, concatenar '0' e somar.
              offlong(15 downto 4) := (others=>'0');
            end if;
            for i in 0 to 15 loop
              carry(i+1) := (counter(i) and offlong(i)) or (counter(i) and carry(i)) or (offlong(i) and carry(i));
              counter(i) := counter(i) xor offlong(i) xor carry(i);
            end loop;
          else										--Se z=0, não há jump (incrementa-se o PC).
            carry(0) := '1';
            for i in 0 to 15 loop
              carry(i+1) := counter(i) and carry(i);
              counter(i) := counter(i) xor carry(i);
            end loop;						
          end if;
          carry := (others=>'0');
        when others => NULL;
       end case;
      end if;
  end if;
  pc_out <= counter;
  end process;
end architecture behavior;					--Fim
